package com.dge.common.utils

import android.content.Context
import android.graphics.Bitmap
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.text.TextUtils
import android.webkit.MimeTypeMap
import androidx.core.content.FileProvider
import com.blankj.utilcode.util.FileIOUtils
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.UriUtils
import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions
import com.dge.common.LibApp
import com.dge.common.common.AppConfig
import com.dge.common.common.Constants
import timber.log.Timber
import java.io.*
import java.util.concurrent.ThreadLocalRandom

/**
 * 文件工具类
 */
object FileUtil {
    private const val pathDiv = "/"
    val internalCacheDir: File
        get() = File(internalCacheDirPath).apply {
            if (!exists()) {
                mkdirs()
            }
        }
    val internalFilesDir: File
        get() = File(internalFilesDirPath).apply {
            if (!exists()) {
                mkdirs()
            }
        }
    private val internalCacheDirPath = "/data/data/" + AppConfig.getPackageName() + "/cache"
    private val internalFilesDirPath = "/data/data/" + AppConfig.getPackageName() + "/files"
    private var cacheDir: File =
        if (isExternalStorageWritable && LibApp.context.externalCacheDir != null) LibApp.context.externalCacheDir!! else LibApp.context.cacheDir
    val APP_CAMERA_PATH: String
        get() =
            if (XXPermissions.isGranted(LibApp.context, Permission.MANAGE_EXTERNAL_STORAGE)) {
                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).absolutePath + pathDiv + "dge" + pathDiv
            } else {
                LibApp.context.getExternalFilesDir(Environment.DIRECTORY_DCIM)!!.absolutePath + pathDiv
            }
    val APP_DOWNLOAD_PATH: String
        get() =
            if (XXPermissions.isGranted(LibApp.context, Permission.MANAGE_EXTERNAL_STORAGE)) {
                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + pathDiv + "dge" + pathDiv
            } else {
                LibApp.context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!.absolutePath + pathDiv
            }
    val random: ThreadLocalRandom get() = ThreadLocalRandom.current()

    fun getFileFromUri(context: Context, uri: Uri, fileName: String): File {
        Timber.i("getFileFromUri uri is  $uri")
        var file = UriUtils.uri2FileNoCacheCopy(uri)
        if (file == null || !file.exists()) {
            file = getFileFromContentUri(context, uri, fileName)
            Timber.i("getFileFromUri file is not valid path = ${file.absolutePath}")
        } else {
            Timber.i("getFileFromUri file is valid path = ${file.absolutePath}")
        }
        return file
    }

    fun getRandomFileName(originalFileName: String): String {
        // val name = "${
        //     System.currentTimeMillis() + random.nextInt(0, Int.MAX_VALUE)
        // }.${getFileType(originalFileName)}"
        val name = "${
            System.currentTimeMillis() + random.nextInt(0, Int.MAX_VALUE)
        }-$originalFileName"
        Timber.i("getRandomFileName name = $name")
        return name
    }

    /**
     * 耗时操作，请在子线程中调用
     */
    fun getFileFromContentUri(context: Context, uri: Uri, fileName: String?): File {
        Timber.d("getFileFromContentUri uri.getScheme() = %s", uri.scheme)
        if (uri.scheme == null) {
            return File(uri.toString())
        }
        if ("file" == uri.scheme) {
            return File(uri.toString().substring(7))
        }
        val dir = File(cacheFileDirPath)
        dir.mkdirs()
        val file = File(cacheFileDirPath, fileName ?: "${random.nextInt(0, Int.MAX_VALUE)}.png")
        var os: OutputStream? = null
        var `is`: InputStream? = null
        var fis: FileOutputStream? = null
        val bufferSize = 512 * 1024
        try {
            file.createNewFile()
            fis = FileOutputStream(file)
            os = BufferedOutputStream(fis, bufferSize)
            `is` = context.contentResolver.openInputStream(uri)
            Timber.d("getFileFromContentUri write")
            // copyData(is, os);
            val data = ByteArray(bufferSize)
            var len: Int
            while (`is`!!.read(data).also { len = it } != -1) {
                os.write(data, 0, len)
                Timber.d("getFileFromContentUri len = %s", len)
                os.flush()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            if (`is` != null) try {
                `is`.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
            if (fis != null) try {
                fis.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
            if (os != null) try {
                os.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return file
    }

    /**
     * 耗时操作，请在子线程中调用
     */
    fun createBitmapFile(bitmap: Bitmap, name: String? = null, quality: Int = 100): File {
        val file = File(cacheFileDirPath, name ?: "${random.nextInt(0, Int.MAX_VALUE)}.jpg")
        val bytes = com.blankj.utilcode.util.ImageUtils.bitmap2Bytes(bitmap, Bitmap.CompressFormat.JPEG, quality)
        /*val success = */FileIOUtils.writeFileFromBytesByStream(file, bytes)
        return file
    }

    fun createWebpFile(bitmap: Bitmap, filename: String): String? {
        val file = File(cacheDir.toString(), filename)
        val bytes = com.blankj.utilcode.util.ImageUtils.bitmap2Bytes(bitmap, Bitmap.CompressFormat.WEBP, 50)
        val success = FileIOUtils.writeFileFromBytesByStream(file, bytes)
        return if (success) file.absolutePath else null
    }

    /**
     * 复制单个文件
     */
    fun copyFile(oldPathName: String, newPathName: String): Boolean {
        return FileUtils.copy(oldPathName, newPathName)
    }

    /**
     * 获取缓存文件地址
     */
    fun getCacheFilePath(fileName: String): String {
        return cacheDir.absolutePath + pathDiv + fileName
    }

    /**
     * 获取缓存文件地址
     */
    fun getCacheFilePath(lastPath: String, fileName: String): String {
        return cacheDir.absolutePath + pathDiv + lastPath + pathDiv + fileName
    }

    private val cacheFileDirPath: String
        get() = cacheDir.absolutePath

    /**
     * 判断缓存文件是否存在
     */
    fun isCacheFileExist(fileName: String): Boolean {
        val file = File(getCacheFilePath(fileName))
        return file.exists()
    }

    /**
     * 将数据存储为文件
     *
     * @param data 数据
     */
    fun createFile(data: ByteArray?, filename: String) {
        FileIOUtils.writeFileFromBytesByStream(File(cacheDir, filename), data)
    }

    /**
     * 将数据存储为文件
     *
     * @param input is
     */
    fun createFile(input: InputStream, filename: String): File? {
        val file = File(cacheDir, filename)
        val success = FileIOUtils.writeFileFromIS(File(cacheDir, filename), input)
        return if (success) file else null
    }

    /**
     * 判断缓存文件是否存在
     */
    fun isFileExist(fileName: String, type: String): Boolean {
        if (isExternalStorageWritable) {
            val dir = LibApp.context.getExternalFilesDir(type)
            if (dir != null) {
                val f = File(dir, fileName)
                return f.exists()
            }
        }
        return false
    }

    /**
     * 将数据存储为文件
     *
     * @param data     数据
     * @param fileName 文件名
     * @param type     文件类型
     */
    fun createFile(data: ByteArray, fileName: String, type: String): File? {
        if (isExternalStorageWritable) {
            val dir = LibApp.context.getExternalFilesDir(type)
            val file = File(dir, fileName)
            val success = FileIOUtils.writeFileFromBytesByStream(file, data)
            return if (success) {
                file
            } else {
                null
            }
        }
        return null
    }

    /**
     * 字节输入流
     *
     * @param inputStream 字节输入流
     * @param fileName    文件名
     */
    @Throws(IOException::class)
    fun createTemporalFileFrom(inputStream: InputStream?, fileName: String): File? {
        var targetFile: File? = null
        if (inputStream != null) {
            var read: Int
            val buffer = ByteArray(8 * 1024)
            //自己定义拷贝文件路径
            targetFile = File(cacheDir, fileName)
            if (targetFile.exists()) {
                targetFile.delete()
            }
            val outputStream: OutputStream = FileOutputStream(targetFile)
            while (inputStream.read(buffer).also { read = it } != -1) {
                outputStream.write(buffer, 0, read)
            }
            outputStream.flush()
            try {
                outputStream.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return targetFile
    }

    fun getMimeType(filePath: String): String {
        var ext = MimeTypeMap.getFileExtensionFromUrl(filePath)
        if (TextUtils.isEmpty(ext)) {
            ext = filePath.substring(filePath.lastIndexOf(".") + 1)
        }
        var type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext)
        if (TextUtils.isEmpty(type)) {
            for (i in MIME_MapTable.indices) {
                if (ext == MIME_MapTable[i][0]) type = MIME_MapTable[i][1]
            }
        }
        Timber.d("openFile type = $type, ext = $ext")
        return type ?: "*/*"
    }

    /**
     * 判断外部存储是否可用
     */
    val isExternalStorageWritable: Boolean
        get() {
            val state = Environment.getExternalStorageState()
            if (Environment.MEDIA_MOUNTED == state) {
                return true
            }
            Timber.e("ExternalStorage not mounted")
            return false
        }

    fun isMediaDocument(uri: Uri): Boolean {
        return "com.android.providers.media.documents" == uri.authority
    }

    fun isExternalStorageDocument(uri: Uri): Boolean {
        return "com.android.externalstorage.documents" == uri.authority
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    fun isDownloadsDocument(uri: Uri): Boolean {
        return "com.android.providers.downloads.documents" == uri.authority
    }

    /**
     * 使用第三方qq文件管理器打开
     *
     * @param uri
     * @return
     */
    fun isQQMediaDocument(uri: Uri): Boolean {
        return "com.tencent.mtt.fileprovider" == uri.authority
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    fun isGooglePhotosUri(uri: Uri): Boolean {
        return "com.google.android.apps.photos.content" == uri.authority
    }

    fun readFileSize(path: String): String {
        return FileUtils.getSize(path)
    }

    /**
     * 获取图片的URI，根据不同版本生成相应的URI
     *
     * @param file
     * @return
     */
    private fun getUriFromFile(file: File): Uri {
        val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            try {
                FileProvider.getUriForFile(LibApp.context, Constants.fileProviderAuthority, file)
            } catch (e: java.lang.Exception) {
                Uri.fromFile(file)
            }
        } else {
            Uri.fromFile(file)
        }
        return uri
    }

    fun refreshAlbum(filePath: String) {
        MediaScannerConnection.scanFile(
            LibApp.context,
            arrayOf(filePath),
            arrayOf(getMimeType(filePath)),
            null
        )
    }

    val MIME_MapTable = arrayOf(
        arrayOf(".3gp", "video/3gpp"),
        arrayOf(".apk", "application/vnd.android.package-archive"),
        arrayOf(".asf", "video/x-ms-asf"),
        arrayOf(".avi", "video/x-msvideo"),
        arrayOf(".bin", "application/octet-stream"),
        arrayOf(".bmp", "image/bmp"),
        arrayOf(".c", "text/plain"),
        arrayOf(".class", "application/octet-stream"),
        arrayOf(".conf", "text/plain"),
        arrayOf(".cpp", "text/plain"),
        arrayOf(".doc", "application/msword"),
        arrayOf(".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
        arrayOf(".xls", "application/vnd.ms-excel"),
        arrayOf(".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
        arrayOf(".exe", "application/octet-stream"),
        arrayOf(".gif", "image/gif"),
        arrayOf(".gtar", "application/x-gtar"),
        arrayOf(".gz", "application/x-gzip"),
        arrayOf(".h", "text/plain"),
        arrayOf(".htm", "text/html"),
        arrayOf(".html", "text/html"),
        arrayOf(".jar", "application/java-archive"),
        arrayOf(".java", "text/plain"),
        arrayOf(".jpeg", "image/jpeg"),
        arrayOf(".jpg", "image/jpeg"),
        arrayOf(".js", "application/x-javascript"),
        arrayOf(".log", "text/plain"),
        arrayOf(".m3u", "audio/x-mpegurl"),
        arrayOf(".m4a", "audio/mp4a-latm"),
        arrayOf(".m4b", "audio/mp4a-latm"),
        arrayOf(".m4p", "audio/mp4a-latm"),
        arrayOf(".m4u", "video/vnd.mpegurl"),
        arrayOf(".m4v", "video/x-m4v"),
        arrayOf(".mov", "video/quicktime"),
        arrayOf(".mp2", "audio/x-mpeg"),
        arrayOf(".mp3", "audio/x-mpeg"),
        arrayOf(".mp4", "video/mp4"),
        arrayOf(".mpc", "application/vnd.mpohun.certificate"),
        arrayOf(".mpe", "video/mpeg"),
        arrayOf(".mpeg", "video/mpeg"),
        arrayOf(".mpg", "video/mpeg"),
        arrayOf(".mpg4", "video/mp4"),
        arrayOf(".mpga", "audio/mpeg"),
        arrayOf(".msg", "application/vnd.ms-outlook"),
        arrayOf(".ogg", "audio/ogg"),
        arrayOf(".pdf", "application/pdf"),
        arrayOf(".png", "image/png"),
        arrayOf(".pps", "application/vnd.ms-powerpoint"),
        arrayOf(".ppt", "application/vnd.ms-powerpoint"),
        arrayOf(".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"),
        arrayOf(".prop", "text/plain"),
        arrayOf(".rc", "text/plain"),
        arrayOf(".rmvb", "audio/x-pn-realaudio"),
        arrayOf(".rtf", "application/rtf"),
        arrayOf(".sh", "text/plain"),
        arrayOf(".tar", "application/x-tar"),
        arrayOf(".tgz", "application/x-compressed"),
        arrayOf(".txt", "text/plain"),
        arrayOf(".wav", "audio/x-wav"),
        arrayOf(".wma", "audio/x-ms-wma"),
        arrayOf(".wmv", "audio/x-ms-wmv"),
        arrayOf(".wps", "application/vnd.ms-works"),
        arrayOf(".xml", "text/plain"),
        arrayOf(".z", "application/x-compress"),
        arrayOf(".zip", "application/x-zip-compressed"),
        arrayOf("", "*/*")
    )

    /***
     * 获取文件类型
     *
     * @param paramString
     * @return
     */
    fun getFileType(paramString: String): String {
        var str = ""
        if (TextUtils.isEmpty(paramString)) {
            if (AppConfig.isDebug()) Timber.d("paramString---->null")
            return str
        }
        if (AppConfig.isDebug()) Timber.d("paramString: %s", paramString)
        val i = paramString.lastIndexOf('.')
        if (i <= -1) {
            if (AppConfig.isDebug()) Timber.d("i <= -1")
            return str
        }
        str = paramString.substring(i + 1)
        if (str.contains("?")) {
            str = str.substring(0, str.indexOf("?"))
        }
        if (AppConfig.isDebug()) Timber.d("suffix: %s", str)
        return str
    }
}